package com.hirsi.module.auth.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.StrUtil;
import com.hirsi.common.core.constant.RedisConstant;
import com.hirsi.common.core.enums.DeveloperStatusEnum;
import com.hirsi.common.core.enums.ExceptionStatusEnum;
import com.hirsi.common.core.enums.IVoDataValidEnum;
import com.hirsi.common.core.exception.ServerRuntimeException;
import com.hirsi.common.core.exception.VoDataValidError;
import com.hirsi.common.core.util.CommonUtil;
import com.hirsi.common.core.util.EncryptUtil;
import com.hirsi.common.core.util.HttpContextUtil;
import com.hirsi.common.db.developer.entity.Developer;
import com.hirsi.common.db.developer.entity.DeveloperApply;
import com.hirsi.common.db.developer.entity.DeveloperAudit;
import com.hirsi.common.db.developer.service.IDeveloperApplyService;
import com.hirsi.common.db.developer.service.IDeveloperAuditService;
import com.hirsi.common.db.developer.service.IDeveloperService;
import com.hirsi.common.module.auth.enums.AuthValidEnum;
import com.hirsi.common.module.auth.service.AuthService;
import com.hirsi.core.DeveloperConstant;
import com.hirsi.module.auth.convert.DeveloperAuthConvert;
import com.hirsi.module.auth.enums.DeveloperAuthValidEnum;
import com.hirsi.module.auth.vo.param.RegisterAccountParam;
import com.hirsi.module.auth.vo.param.RegisterCheckPhoneParam;
import com.hirsi.module.auth.vo.param.RegisterSendSmsParam;
import com.hirsi.module.auth.vo.result.RegisterAccountDetailResult;
import com.hirsi.module.auth.vo.result.RegisterAccountSecretResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class DeveloperAuthService extends AuthService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private IDeveloperService iDeveloperService;

    @Autowired
    private IDeveloperApplyService iDeveloperApplyService;

    @Autowired
    private IDeveloperAuditService iDeveloperAuditService;

    @Value("${spring.application.name}")
    private String applicationName;

    //正式环境禁止的一些操作
    @Value("${knife4j.production:#{false}}")
    private Boolean isProd;

    public void sendSmsCode(RegisterSendSmsParam param) {
        String captchaKey = super.checkCaptcha(param);
        Developer developer = iDeveloperService.lambdaQuery()
                .eq(Developer::getPhone, EncryptUtil.encrypt(param.getPhone()))
                .select(Developer::getStatus).last("limit 1").one();
        if (developer != null && DeveloperStatusEnum.ENABLE.getValue().equals(developer.getStatus())) {
            throw new ServerRuntimeException(new VoDataValidError(DeveloperAuthValidEnum.PHONE_REGISTERED));
        }
        //此处发送短信验证码
        if (StrUtil.isNotBlank(captchaKey)) redisTemplate.delete(captchaKey);
    }

    public RegisterAccountDetailResult checkPhone(RegisterCheckPhoneParam param) {
        String code = redisTemplate.opsForValue().get(String.format(RedisConstant.PHONE_COKE_KEY_PREFIX, applicationName, param.getPhone()));
        if (StrUtil.isBlank(code)) throw new ServerRuntimeException(new VoDataValidError(AuthValidEnum.CODE_INVALID));
        else if (!param.getPhoneCode().equals(code)) throw new ServerRuntimeException(new VoDataValidError(AuthValidEnum.CODE_ERROR));
        Developer developer = iDeveloperService.lambdaQuery()
                .eq(Developer::getPhone, EncryptUtil.encrypt(param.getPhone()))
                .select(Developer::getId, Developer::getName, Developer::getPhone, Developer::getIdCard, Developer::getProvinceId,
                        Developer::getCityId, Developer::getDistrictId, Developer::getStreetId, Developer::getAddress, Developer::getAvatar,
                        Developer::getNick, Developer::getEmail, Developer::getUsername, Developer::getStatus)
                .last("limit 1").one();
        if (developer != null && DeveloperStatusEnum.ENABLE.getValue().equals(developer.getStatus())) {
            throw new ServerRuntimeException(new VoDataValidError(DeveloperAuthValidEnum.PHONE_REGISTERED));
        }
        RegisterAccountDetailResult result = DeveloperAuthConvert.INSTANCE.developerToRegisterAccountDetailResult(developer);
        //注册记录
        DeveloperApply developerApply = iDeveloperApplyService.lambdaQuery()
                .eq(DeveloperApply::getDeveloperId, result.getId())
                .select(DeveloperApply::getRemark)
                .orderByDesc(DeveloperApply::getCreateTime)
                .last("limit 1").one();
        if (developerApply != null) result.setRemark(developerApply.getRemark());

        //审核回复
        DeveloperAudit developerAudit = iDeveloperAuditService.lambdaQuery()
                .eq(DeveloperAudit::getDeveloperId, result.getId())
                .select(DeveloperAudit::getStatus, DeveloperAudit::getReply)
                .orderByDesc(DeveloperAudit::getCreateTime)
                .last("limit 1").one();
        if (developerAudit != null) {
            result.setReply(developerAudit.getReply());
            result.setAuditTime(developerAudit.getCreateTime());
        }
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    public RegisterAccountSecretResult register(RegisterAccountParam param) {
        List<IVoDataValidEnum> dataValidEnums = new ArrayList<>();
        String key = String.format(RedisConstant.PHONE_COKE_KEY_PREFIX, applicationName, param.getPhone());
        if (isProd) {
            String code = redisTemplate.opsForValue().get(key);
            if (StrUtil.isBlank(code)) dataValidEnums.add(AuthValidEnum.CODE_INVALID);
            else if (!param.getPhoneCode().equals(code)) dataValidEnums.add(AuthValidEnum.CODE_ERROR);
        }
        if (StrUtil.isNotBlank(param.getPassword()) && !param.getPassword().equals(param.getConfirmPassword())) {
            dataValidEnums.add(DeveloperAuthValidEnum.PASSWORD_ERROR);
            dataValidEnums.add(DeveloperAuthValidEnum.CONFIRM_PASSWORD_ERROR);
        }
        if (CollUtil.isNotEmpty(dataValidEnums)) throw new ServerRuntimeException(new VoDataValidError(dataValidEnums));
        if (param.getId() != null) {
            Developer oldDeveloper = iDeveloperService.lambdaQuery()
                    .eq(Developer::getId, param.getId())
                    .select(Developer::getStatus).last("limit 1").one();
            if (oldDeveloper != null) {
                //审核中的不能修改注册信息
                if (DeveloperStatusEnum.AUDIT.getValue().equals(oldDeveloper.getStatus())) {
                    throw new ServerRuntimeException(DeveloperAuthValidEnum.ACCOUNT_AUDIT.getMsg());
                } else if (!DeveloperStatusEnum.REJECT.getValue().equals(oldDeveloper.getStatus())) {
                    throw new ServerRuntimeException("注册账号已审核过，请登录");
                }
            } else param.setId(null);
        }
        List<Developer> tempDevelopers = iDeveloperService.lambdaQuery()
                .ne(param.getId() != null, Developer::getId, param.getId())
                .and(e -> e.eq(Developer::getPhone, EncryptUtil.encrypt(param.getPhone()))
                        .or().eq(Developer::getIdCard, EncryptUtil.encrypt(param.getIdCard()))
                        .or().eq(Developer::getCode, param.getCode())
                        .or(StrUtil.isNotBlank(param.getUsername())).eq(Developer::getUsername, param.getUsername())
                        .or(StrUtil.isNotBlank(param.getNick())).eq(Developer::getNick, param.getNick())
                        .or(StrUtil.isNotBlank(param.getEmail())).eq(Developer::getEmail, param.getEmail()))
                .select(Developer::getId, Developer::getPhone, Developer::getIdCard, Developer::getCode, Developer::getUsername,
                        Developer::getNick, Developer::getEmail ,Developer::getStatus)
                .list();
        //重复注册判断
        if (CollUtil.isNotEmpty(tempDevelopers)) {
            long count = tempDevelopers.stream().filter(item -> item.getPhone().equals(EncryptUtil.encrypt(param.getPhone()))).count();
            if (count > 0) dataValidEnums.add(DeveloperAuthValidEnum.PHONE_REGISTERED);
            count = tempDevelopers.stream().filter(item -> item.getIdCard().equals(EncryptUtil.encrypt(param.getIdCard()))).count();
            if (count > 0) dataValidEnums.add(DeveloperAuthValidEnum.ID_CARD_REGISTERED);
            count = tempDevelopers.stream().filter(item -> item.getCode().equals(param.getCode())).count();
            if (count > 0) dataValidEnums.add(DeveloperAuthValidEnum.CODE_REGISTERED);
            if (StrUtil.isNotBlank(param.getUsername())) {
                count = tempDevelopers.stream().filter(item -> param.getUsername().equals(item.getUsername())).count();
                if (count > 0) dataValidEnums.add(DeveloperAuthValidEnum.USERNAME_REGISTERED);
            }
            if (StrUtil.isNotBlank(param.getNick())) {
                count = tempDevelopers.stream().filter(item -> param.getNick().equals(item.getNick())).count();
                if (count > 0) dataValidEnums.add(DeveloperAuthValidEnum.NICK_REGISTERED);
            }
            if (StrUtil.isNotBlank(param.getEmail())) {
                count = tempDevelopers.stream().filter(item -> param.getEmail().equals(item.getEmail())).count();
                if (count > 0) dataValidEnums.add(DeveloperAuthValidEnum.EMAIL_REGISTERED);
            }
        }
        if (CollUtil.isNotEmpty(dataValidEnums)) throw new ServerRuntimeException(new VoDataValidError(dataValidEnums));
        Developer developer = DeveloperAuthConvert.INSTANCE.registerAccountParamToDeveloper(param);
        developer.setBirthDate(IdcardUtil.getBirthDate(developer.getIdCard()).toLocalDateTime().toLocalDate())
                .setGender(IdcardUtil.getGenderByIdCard(developer.getIdCard()) == 0 ? 1 : 0)
                .setStatus(DeveloperStatusEnum.AUDIT.getValue());
        DeveloperApply developerApply = new DeveloperApply().setRemark(param.getRemark()).setCreateIp(HttpContextUtil.getIpAddr());
        if (StrUtil.isBlank(developer.getNick())) developer.setNick(String.format("%s%s", developer.getName(), developer.getPhone().substring(8)));
        if (StrUtil.isBlank(developer.getUsername())) developer.setUsername(developer.getPhone());
        if (StrUtil.isBlank(developer.getPassword())) {
            String password = CommonUtil.generatePassword();
            developerApply.setAutoPassword(password);
            developer.setPassword(cn.hutool.crypto.SecureUtil.md5(password));
        }
        //身份证和手机号加秘
        developer.setPhone(EncryptUtil.encrypt(developer.getPhone())).setIdCard(EncryptUtil.encrypt(developer.getIdCard()));
        developer.insertOrUpdate();
        developerApply.setDeveloperId(developer.getId()).insert();
        String encrypt = EncryptUtil.encrypt(developer.getId().toString());
        redisTemplate.opsForValue().set(String.format(DeveloperConstant.REGISTER_SECRET_KEY_PREFIX, applicationName, developer.getId()), encrypt, DeveloperConstant.REGISTER_SECRET_KEY_EXPIRE_TIME, TimeUnit.SECONDS);
        redisTemplate.delete(key);
        return new RegisterAccountSecretResult(encrypt, System.currentTimeMillis() - 5000 + DeveloperConstant.REGISTER_SECRET_KEY_EXPIRE_TIME * 1000);
    }

    public RegisterAccountDetailResult registerAccountDetail(String secretKey) {
        try {
            Integer id = Integer.parseInt(EncryptUtil.publicKeyDecrypt(secretKey));
            Developer developer = iDeveloperService.lambdaQuery()
                    .eq(Developer::getId, id)
                    .select(Developer::getId, Developer::getName, Developer::getPhone, Developer::getIdCard, Developer::getCode,
                            Developer::getProvinceId, Developer::getCityId, Developer::getDistrictId, Developer::getStreetId, Developer::getAddress,
                            Developer::getAvatar, Developer::getNick, Developer::getEmail, Developer::getUsername, Developer::getStatus)
                    .last("limit 1").one();
            if (developer == null) throw new ServerRuntimeException(new VoDataValidError(AuthValidEnum.USER_NOT_EXIST));
            RegisterAccountDetailResult result = DeveloperAuthConvert.INSTANCE.developerToRegisterAccountDetailResult(developer);
            //注册记录
            DeveloperApply developerApply = iDeveloperApplyService.lambdaQuery()
                    .eq(DeveloperApply::getDeveloperId, result.getId())
                    .select(DeveloperApply::getRemark)
                    .orderByDesc(DeveloperApply::getCreateTime)
                    .last("limit 1").one();
            if (developerApply != null) result.setRemark(developerApply.getRemark());

            //审核回复
            DeveloperAudit developerAudit = iDeveloperAuditService.lambdaQuery()
                    .eq(DeveloperAudit::getDeveloperId, result.getId())
                    .select(DeveloperAudit::getStatus, DeveloperAudit::getReply, DeveloperAudit::getCreateTime)
                    .orderByDesc(DeveloperAudit::getCreateTime)
                    .last("limit 1").one();
            if (developerAudit != null) {
                result.setReply(developerAudit.getReply());
                result.setAuditTime(developerAudit.getCreateTime());
            }
            return result;
        } catch (Exception e) {
            throw e;
        }
    }
}
